#pragma once

#include <iostream>
#include <cmath>
#include "vec2.h"
#include "vec3.h"

struct Vec4
{
	float  x;
	float  y;
	float  z;
	float  w;

	Vec4( float s = float(0.0) )
		: x(s), y(s), z(s), w(s)
	{
		//
	}

	Vec4( float x, float y, float z, float w ) :
		x(x), y(y), z(z), w(w)

	{
		//
	}

	Vec4( const Vec4& v )
	{
		x = v.x;
		y = v.y;
		z = v.z;
		w = v.w;
	}

	Vec4( const Vec3& v, const float w = 1.0 ) :
		w(w)
	{
		x = v.x;
		y = v.y;
		z = v.z;
	}

	Vec4( const Vec2& v, const float z, const float w ) :
		z(z), w(w)
	{
		x = v.x;
		y = v.y;
	}

	float& operator [] ( int i ) { return *(&x + i); }
	const float operator [] ( int i ) const { return *(&x + i); }

	float dot( const Vec4& v ) const
	{
		return x*v.x + y*v.y + z*v.z + w * v.w;
	}

	float length() const
	{
		return std::sqrt( dot(*this) );
	}

	Vec4& normalize()
	{
		*this /= length();
		return *this;
	}

	Vec3 cross(const Vec4& v ) const
	{
		return Vec3(
			y * v.z - z * v.y,
			z * v.x - x * v.z,
			x * v.y - y * v.x );
	}

	Vec4 operator - () const
	{
		return Vec4( -x, -y, -z, -w );
	}

	Vec4 operator + ( const Vec4& v ) const
	{
		return Vec4( x + v.x, y + v.y, z + v.z, w + v.w );
	}

	Vec4 operator - ( const Vec4& v ) const
	{
		return Vec4( x - v.x, y - v.y, z - v.z, w - v.w );
	}

	Vec4 operator * ( const float s ) const
	{
		return Vec4( s*x, s*y, s*z, s*w );
	}

	Vec4 operator * ( const Vec4& v ) const
	{
		return Vec4( x*v.x, y*v.y, z*v.z, w*v.w );
	}

	friend Vec4 operator * ( const float s, const Vec4& v )
	{
		return v * s;
	}

	Vec4 operator / ( const float s ) const
	{
		float r = float(1.0) / s;
		return *this * r;
	}

	Vec4& operator += ( const Vec4& v )
	{
		x += v.x; 
		y += v.y;
		z += v.z;
		w += v.w;  
		return *this;
	}

	Vec4& operator -= ( const Vec4& v )
	{
		x -= v.x;
		y -= v.y;
		z -= v.z; 
		w -= v.w;
		return *this;
	}

	Vec4& operator *= ( const float s )
	{
		x *= s;
		y *= s; 
		z *= s;
		w *= s; 
		return *this; 
	}

	Vec4& operator *= ( const Vec4& v )
	{
		x *= v.x;
		y *= v.y;
		z *= v.z;
		w *= v.w;
		return *this;
	}

	Vec4& operator /= ( const float s )
	{
		float r = float(1.0) / s;
		*this *= r;

		return *this;
	}
	
	friend std::ostream& operator << ( std::ostream& os, const Vec4& v )
	{
		return os << "( " << v.x << ", " << v.y
			<< ", " << v.z << ", " << v.w << " )";
	}

	friend std::istream& operator >> ( std::istream& is, Vec4& v )
	{
		return is >> v.x >> v.y >> v.z >> v.w;
	}

	operator const float* () const
	{
		return static_cast<const float*>( &x );
	}

	operator float* ()
	{
		return static_cast<float*>( &x );
	}
};
